package com.itheima.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.itheima.es.entity.HotelEntity;
import com.itheima.es.mapper.HotelMapper;
import com.itheima.es.service.HotelService;
import org.apache.commons.lang3.ObjectUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class HotelServiceImpl implements HotelService {

    @Autowired
    private HotelMapper hotelMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    //todo 从mysql批量导入到es: restHighLevelClient.bulk
    @Override
    public int addDocToES() {

        //BulkRequest可以使用一个请求执行多个索引、更新或删除操作
        BulkRequest bulkRequest = new BulkRequest();

        //查询mysql数据库
        //mybatis-plus的查询所有api
        List<HotelEntity> hotelEntityList = hotelMapper.selectList(null);

        //遍历集合
        for (HotelEntity hotelEntity : hotelEntityList) {

            //hotelEntity对象 里面有日期类型,就可以自动转换格式 将对象转化为Json字符串
            String jsonStr = JSON.toJSONStringWithDateFormat(hotelEntity, "yyyy-MM-dd", SerializerFeature.WriteDateUseDateFormat);

            //IndexRequest indexRequest = new IndexRequest("索引", "type","id");
            //indexRequest.source("转换为json的对象", XContentType.JSON);
            IndexRequest indexRequest = new IndexRequest("hotel").source(jsonStr, XContentType.JSON);

            //加入到批量请求bulk
            bulkRequest.add(indexRequest);
        }

        try {
            //es的批量插入
            BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

            return response.status().getStatus();

        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    //todo 查询全部: QueryBuilders.matchAllQuery
    @Override
    public Map<String, Object> matchAllQuery() {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        /**
         * 对搜索行为的配置可以使用 SearchSourceBuilder 来完成
         *
         * SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();  // 默认配置
         * sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); // 设置搜索，可以是任何类型的 QueryBuilder
         * sourceBuilder.from(0); // 起始 index
         * sourceBuilder.size(5); // 大小 size
         * sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); // 设置搜索的超时时间
         *
         * 设置完成后，就可以添加到 SearchRequest 中。
         * SearchRequest searchRequest = new SearchRequest();
         * searchRequest.source(sourceBuilder);
         */

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.matchAllQuery() ==> match_all 查询
        MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

        //为搜索的文档内容对象SearchSourceBuilder设置参数,添加 match_all 查询
        searchSourceBuilder.query(queryBuilder);

        //将SearchSourceBuilder对象 添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {
            //es的查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //获取hits
            SearchHits hits = searchResponse.getHits();
            //获取总条数
            long total = hits.getTotalHits().value;
            //获取hits数组内容
            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            //遍历hits数组
            for (SearchHit searchHit : searchHits) {
                //
                String sourceAsString = searchHit.getSourceAsString();

                //JSON.parseObject() 将Json字符串转化为相应的对象
                //JSON.toJSONString() 将对象转化为Json字符串
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                list.add(hotelEntity);
            }
            //返回值用Map接收
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", total);

            return map;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 分页查询: QueryBuilders.matchAllQuery
    @Override
    public Map<String, Object> pageQuery(int current, int size) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.matchAllQuery() 查询所有
        MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

        //为搜索的文档内容对象SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);

        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {
            //es的查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //获取hits
            SearchHits hits = searchResponse.getHits();
            //获取总条数
            long total = hits.getTotalHits().value;
            //获取数组内容
            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            //遍历数组
            for (SearchHit searchHit : searchHits) {
                //
                String sourceAsString = searchHit.getSourceAsString();

                //JSON.parseObject() 将Json字符串转化为相应的对象
                //JSON.toJSONString() 将对象转化为Json字符串
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                list.add(hotelEntity);
            }
            //Map接收
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", total);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (total + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    //todo 按照品牌精确查询: QueryBuilders.termQuery
    @Override
    public Map<String, Object> brandTermQuery(int current, int size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //termQuery 精确查询
        TermQueryBuilder queryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand").toString());

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {

            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();
            //获取总条数
            long total = hits.getTotalHits().value;
            //获取数组
            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            //遍历数组
            for (SearchHit searchHit : searchHits) {

                String sourceAsString = searchHit.getSourceAsString();

                //JSON.parseObject() 将Json字符串转化为相应的对象
                //JSON.toJSONString() 将对象转化为Json字符串
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                list.add(hotelEntity);
            }
            //Map接收
            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", total);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (total + size - 1) / size);

            return map;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    //todo 根据酒店名称分词查询: QueryBuilders.matchQuery
    @Override
    public Map<String, Object> nameMatchQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //match 分词查询
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", searchParam.get("name"));

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        //处理查询结果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long total = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();

                //JSON.parseObject() 将Json字符串转化为相应的对象
                //JSON.toJSONString() 将对象转化为Json字符串
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                list.add(hotelEntity);
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", total);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (total + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 根据酒店品牌模糊查询: QueryBuilders.wildcardQuery
    @Override
    public Map<String, Object> nameWildcardQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //设置查询
        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        WildcardQueryBuilder queryBuilder = QueryBuilders.wildcardQuery("brand", searchParam.get("name") + "*");

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();

                //JSON.parseObject() 将Json字符串转化为相应的对象
                //JSON.toJSONString() 将对象转化为Json字符串
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
                list.add(hotelEntity);
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 多域查询: QueryBuilders.queryStringQuery().file()....defaultOperator(Operator.OR);
    @Override
    public Map<String, Object> searchQueryStringQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //QueryBuilders.queryStringQuery 模糊查询
        QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                //设置多个域
                .field("name")
                .field("synopsis")
                .field("area")
                .field("address")
                //取或 取与
                .defaultOperator(Operator.OR);

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long total = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", total);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (total + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 搜索框自动提示: Completion Suggest
    @Override
    public List<String> searchSuggestInfo(String key) {

        //定义结果集
        List<String> resultList = new ArrayList<>();

        //设置查询
        SearchRequest searchRequest = new SearchRequest("suggest");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //todo 构建自动补全搜索
        SuggestBuilder suggestBuilder = new SuggestBuilder();

        //添加别名
        CompletionSuggestionBuilder completionSuggestionBuilder = SuggestBuilders.completionSuggestion("name")
                .text(key)
                .size(10);
        suggestBuilder.addSuggestion("my-suggest", completionSuggestionBuilder);
        //为SearchSourceBuilder设置参数
        searchSourceBuilder.suggest(suggestBuilder);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //todo 处理自动补全查询结果
            //获取suggest对象
            Suggest suggest = searchResponse.getSuggest();

            //获取suggest里的my-suggest,得到completionSuggestion对象(数组)
            CompletionSuggestion completionSuggestion = suggest.getSuggestion("my-suggest");
            //my-suggest后的一个集合
            List<CompletionSuggestion.Entry> entries = completionSuggestion.getEntries();

            Map<String, Object> map = new HashMap<>();
            //entries判断非空
            if (entries != null && entries.size() > 0) {
                //遍历entries
                for (CompletionSuggestion.Entry entry : entries) {

                    //得到entry里的options
                    List<CompletionSuggestion.Entry.Option> options = entry.getOptions();

                    if (ObjectUtils.isNotEmpty(options)) {
                        //遍历options
                        for (CompletionSuggestion.Entry.Option option : options) {
                            //得到option里的text:""
                            String suggestInfo = option.getText().toString();

                            //suggestInfo判断
                            if (key.equals(suggestInfo)) {
                                //用户输入的搜索条件和拿到的是否一样
                                continue;
                            }
                            //定义的集合
                            resultList.add(suggestInfo);
                        }
                    } else {
                        //数据填充,为了后面的数据搜索
                        map.put("name", key);
                    }
                }

            }

            //todo 向索引库增量添加查询条件
            if (ObjectUtils.isNotEmpty(map)) {

                IndexRequest indexRequest = new IndexRequest("suggest").source(map);
                try {
                    //向索引中添加文档
                    restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("list" + resultList);

            return resultList;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    //todo 根据销量排序查询: searchSourceBuilder.sort("salesVolume", SortOrder.DESC)
    @Override
    public Map<String, Object> salesSortQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //todo 排序只需要searchSourceBuilder构建就行了  不基于queryBuilder
        //判断 sortWay参数是否为desc
        if ("desc".equals(searchParam.get("sortWay").toString())) {
            // 降序
            searchSourceBuilder.sort("salesVolume", SortOrder.DESC);
        } else {
            // 默认升序
            searchSourceBuilder.sort("salesVolume", SortOrder.ASC);
        }

        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);
            map.put("sortWay", searchParam.get("sortWay"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 根据价格范围查询: QueryBuilders.rangeQuery("price")
    @Override
    public Map<String, Object> priceRangeQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //rangeQuery() gte:大于等于  lt:小于
        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
                .gte(searchParam.get("minPrice"))
                .lt(searchParam.get("maxPrice"));

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //将SearchSourceBuilder对象添加到搜索请求中
        searchRequest.source(searchSourceBuilder);

        //处理查询结果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 多条件查询(多域、品牌精确、价格范围、销量排序): QueryBuilders.boolQuery()
    @Override
    public Map<String, Object> searchBoolQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        /**
         * must（and）：条件必须成立
         * must_not（not）：条件必须不成立
         * should（or）：条件可以成立
         * filter：条件必须成立，性能比must高。
         */
        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置查询方式
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //todo 1.多域查询
        if (ObjectUtils.isNotEmpty(searchParam.get("condition"))) {
            //如果前段传参condition 不为空
            QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                    .field("name")
                    .field("area")
                    .field("address")
                    .field("synopsis")
                    .defaultOperator(Operator.OR);
            //多域查询加入boolQueryBuilder,must
            boolQueryBuilder.must(queryBuilder);
        }
        //todo 2.品牌精确
        if (ObjectUtils.isNotEmpty(searchParam.get("brand"))) {
            //如果前段传参brand 不为空
            TermQueryBuilder queryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand"));
            //品牌精确查询加入boolQueryBuilder,filter
            boolQueryBuilder.filter(queryBuilder);
        }
        //todo 3.价格范围
        if (ObjectUtils.isNotEmpty(searchParam.get("minPrice")) && ObjectUtils.isNotEmpty(searchParam.get("maxPrice"))) {
            //如果前段传参price 不为空
            RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
                    .gte(searchParam.get("minPrice"))
                    .lte(searchParam.get("maxPrice"));
            boolQueryBuilder.filter(queryBuilder);
        }
        //todo 4.销量排序
        //排序只需要searchSourceBuilder构建就行了  不基于queryBuilder
        if (ObjectUtils.isNotEmpty(searchParam.get("sortWay"))) {
            //如果前段传参sortWay 不为空
            if ("desc".equals(searchParam.get("sortWay"))) {
                //降序
                searchSourceBuilder.sort("salesVolume", SortOrder.DESC);
            } else {
                //升序
                searchSourceBuilder.sort("salesVolume", SortOrder.ASC);
            }
        }

        //为SearchSourceBuilder设置参数
        searchSourceBuilder.query(boolQueryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        //处理查询结果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            map.put("brand", searchParam.get("brand"));
            map.put("area", searchParam.get("area"));
            map.put("specs", searchParam.get("specs"));
            map.put("sortWay", searchParam.get("sortWay"));
            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 地址纠错查询:  QueryBuilders.fuzzyQuery()
    @Override
    public Map<String, Object> searchFuzzyQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("hotel");

        //创建 搜索内容参数设置对象:SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置查询方式
        //设置城市为纠错查询
        FuzzyQueryBuilder queryBuilder = QueryBuilders.fuzzyQuery("area", searchParam.get("condition"))
                .fuzziness(Fuzziness.ONE) //纠错1个字
                .prefixLength(1); //前几个字不允许编辑

        searchSourceBuilder.query(queryBuilder);
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        // 处理查询结果
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);

            map.put("brand", searchParam.get("brand"));
            map.put("area", searchParam.get("area"));
            map.put("specs", searchParam.get("specs"));
            map.put("sortWay", searchParam.get("sortWay"));
            map.put("minPrice", searchParam.get("minPrice"));
            map.put("maxPrice", searchParam.get("maxPrice"));
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 按名称高亮查询: HighlightBuilder()
    @Override
    public Map<String, Object> searchHighLight(Integer current, Integer size, Map<String, Object> searchParam) {

        //设置查询
        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置查询方式
        if (ObjectUtils.isNotEmpty(searchParam.get("condition"))) {
            QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                    .field("name")
                    .field("synopsis")
                    .field("area")
                    .field("address")
                    .defaultOperator(Operator.OR);
            searchSourceBuilder.query(queryBuilder);
        }
        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        //todo 1.HighlightBuilder通过searchSourceBuilder构建
            /*
             fields：指定要对哪个域高亮显示
             pre_tags：设置高亮样式前缀
             post_tags：设置高亮样式后缀
             */
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置高亮的域(可以field多个域)
        highlightBuilder.field("name");
        //设置样式(前缀后缀)
        highlightBuilder.preTags("<font color='red'>");
        highlightBuilder.postTags("</font>");

        searchSourceBuilder.highlighter(highlightBuilder);

        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);

                //todo  2.处理高亮结果
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("name");
                //如果高亮域不为空(同时操作多个域,只对name域生效)
                if (highlightField != null) {
                    //操作highlightField
                    Text[] fragments = highlightField.fragments();
                    if (fragments != null) {
                        //覆盖hotelEntity原有的name
                        hotelEntity.setName(fragments[0].toString());
                    }
                }

                list.add(hotelEntity);
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 品牌分组聚合: AggregationBuilders.terms().field().size();
    /*
    在ES中对于聚合查询，主要分为两类：指标(Metric)聚合 与 桶(Bucket)聚合。
        - 指标聚合：max、min、sum等。作用等同于Mysql中的相关聚合函数。
        - 桶聚合：group by，对数据进行分组(不能操作 "text")
    */
    @Override
    public Map<String, Object> searchBrandGroupQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        //设置查询
        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置查询方式
        if (ObjectUtils.isNotEmpty(searchParam.get("condition"))) {
            QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                    .field("name")
                    .field("synopsis")
                    .field("area")
                    .field("address")
                    .defaultOperator(Operator.OR);
            searchSourceBuilder.query(queryBuilder);
        }

        //todo 1.按品牌分组聚合
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("hotel_brand")
                .field("brand") //处理brand域
                .size(100); //显示100条

        searchSourceBuilder.aggregation(aggregationBuilder);

        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            //todo 2.获取并处理聚合查询结果
            //获取aggregations
            Aggregations aggregations = searchResponse.getAggregations();
            //对象转为map
            Map<String, Aggregation> aggregationMap = aggregations.asMap();
            //获取hotel_brand对象,强转Terms
            Terms terms = (Terms) aggregationMap.get("hotel_brand");
            //获取buckets
            List<? extends Terms.Bucket> buckets = terms.getBuckets();

            List<String> brandList = new ArrayList(16);
            //判断 遍历
            if (buckets != null && buckets.size() > 0) {
                buckets.forEach(bucket -> {
                    System.out.println(bucket.getKeyAsString());
                    brandList.add(bucket.getKeyAsString());
                    //添加到list集合后 在装给map接收,返回前段 ↓
                });

            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            //设置品牌分组列表
            map.put("brandList", brandList);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 自定义日期时间段聚合统计某品牌的酒店销量(链式 双重聚合)
    @Override
    public List<Map<String, Object>> searchDateHistogram(Map<String, Object> searchParam) {

        //定义结果集
        List<Map<String, Object>> result = new ArrayList<>();

        //设置查询
        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //todo 1.自定义日期时间段范围查询
        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("createTime")
                .gte(searchParam.get("minTime"))
                .lte(searchParam.get("maxTime"));
        searchSourceBuilder.query(queryBuilder);

        //todo 2.聚合查询设置
        //a.第一层聚合
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("hotel_brand")
                .field("brand")
                .size(100);
        //b.添加二级聚合 求和
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum("salecount")
                .field("salesVolume");
        aggregationBuilder.subAggregation(sumAggregationBuilder);

        searchSourceBuilder.aggregation(aggregationBuilder);

        searchRequest.source(searchSourceBuilder);

        try {

            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //todo 获取聚合结果并处理
            //获取aggregations
            Aggregations aggregations = searchResponse.getAggregations();
            //转换map
            Map<String, Aggregation> aggregationMap = aggregations.asMap();
            Terms terms = (Terms) aggregationMap.get("hotel_brand");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            if (buckets != null && buckets.size() > 0) {
                //遍历
                buckets.forEach(bucket -> {
                    //现在需要返回的不是简单数据,map接收,封装品牌和销量
                    Map<String, Object> map = new HashMap<>(16);
                    //设置品牌名称
                    map.put("brand", bucket.getKeyAsString());

                    //获取二级聚合数据 bucket.getAggregations()
                    Aggregations aggregations1 = bucket.getAggregations();
                    //二次聚合是sum操作,得到ParsedSum对象
                    ParsedSum parsedSum = aggregations1.get("salecount");
                    Integer sumValue = (int) parsedSum.getValue();
                    map.put("sumValue", sumValue);

                    result.add(map);
                });
            }

            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //todo 加权查询 QueryBuilders.functionScoreQuery()
    @Override
    public Map<String, Object> searchScoreQuery(Integer current, Integer size, Map<String, Object> searchParam) {

        SearchRequest searchRequest = new SearchRequest("hotel");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //todo 构建查询
        //1.基准多域查询
        QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                .field("name")
                .field("address")
                .field("area")
                .field("synopsis")
                .defaultOperator(Operator.OR);

         //2.权重查询!!!!!!!
        // String[] str = new String[]{};
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] scoreFunctionBuilder = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                    QueryBuilders.termQuery("isAd",1),ScoreFunctionBuilders.weightFactorFunction(100)
            )
        };

        FunctionScoreQueryBuilder queryBuilder = QueryBuilders.functionScoreQuery(queryStringQueryBuilder, scoreFunctionBuilder);//基准查询 + 权重查询

        searchSourceBuilder.query(queryBuilder);

        //设置分页
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        try {
            //处理查询结果
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits().value;

            SearchHit[] searchHits = hits.getHits();

            List<HotelEntity> list = new ArrayList<>();

            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
            }

            Map<String, Object> map = new HashMap<>();
            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //设置总页数
            map.put("totalPage", (totalHits + size - 1) / size);

            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
